package org.jsola.hr.service.impl;

import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.constant.SocPayCostType;
import org.jsola.hr.dao.ISocDifferenceDAO;
import org.jsola.hr.dto.BachDifferenceDTO;
import org.jsola.hr.dto.SocDifferenceAddDTO;
import org.jsola.hr.dto.SocDifferenceUpdateDTO;
import org.jsola.hr.entity.EmpSocInfoDO;
import org.jsola.hr.entity.EmpSocMonthDO;
import org.jsola.hr.entity.EmpSocRelatedDO;
import org.jsola.hr.entity.SocDifferenceDO;
import org.jsola.hr.query.EmpSocInfoQuery;
import org.jsola.hr.query.SocDifferenceQuery;
import org.jsola.hr.service.IEmpSocInfoService;
import org.jsola.hr.service.IEmpSocMonthService;
import org.jsola.hr.service.IEmpSocRelatedService;
import org.jsola.hr.service.ISocDifferenceService;
import org.jsola.hr.vo.EmpSocInfoListVO;
import org.jsola.hr.vo.SocDifferenceListVO;
import org.jsola.hr.vo.SocDifferenceVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 员工参保差额费用记录
 *
 * @author zhr
 */
@Service("hrSocDifferenceServiceImpl")
public class SocDifferenceServiceImpl implements ISocDifferenceService {

    @Autowired
    private ISocDifferenceDAO socDifferenceDAO;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    @Autowired
    private IEmpSocMonthService empSocMonthService;

    @Autowired
    private IEmpSocRelatedService empSocRelatedService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SocDifferenceVO save(SocDifferenceAddDTO socDifferenceAddDTO, TokenUser tokenUser) {
        // 转DO
        SocDifferenceDO socDifferenceDO = socDifferenceAddDTO.to(SocDifferenceDO.class);
        // 保存
        socDifferenceDO = save(socDifferenceDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return socDifferenceDO.to(SocDifferenceVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(SocDifferenceUpdateDTO socDifferenceUpdateDTO, TokenUser tokenUser) {
        //转DO
        SocDifferenceDO socDifferenceDO = socDifferenceUpdateDTO.to(SocDifferenceDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(socDifferenceDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... socDifferenceIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) socDifferenceIds);
    }


    @Override
    public SocDifferenceVO selectById(String socDifferenceId, String siteId) {
        SocDifferenceDO socDifferenceDO = selectDOById(socDifferenceId, siteId);
        if (socDifferenceDO == null) {
            return null;
        }
        return socDifferenceDO.to(SocDifferenceVO.class);
    }

    @Override
    public List<SocDifferenceListVO> select(SocDifferenceQuery socDifferenceQuery, String siteId) {
        List<SocDifferenceDO> socDifferenceDOList = selectDO(socDifferenceQuery, siteId);
        if (CollectionUtils.isEmpty(socDifferenceDOList)) {
            return socDifferenceDOList == null ? null : new ArrayList<>();
        }
        return socDifferenceDOList.stream()
                .map(socDifferenceDO -> socDifferenceDO.to(SocDifferenceListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(SocDifferenceQuery socDifferenceQuery, String siteId) {
        Example example = buildExample(socDifferenceQuery, siteId);
        return socDifferenceDAO.selectCountByExample(example);
    }

    @Override
    public Page<SocDifferenceListVO> selectPage(SocDifferenceQuery socDifferenceQuery, String siteId) {
        Example example = buildExample(socDifferenceQuery, siteId);
        Page<SocDifferenceDO> page = socDifferenceDAO.selectPageByExample(example,
                socDifferenceQuery.getPageNo(),
                socDifferenceQuery.getPageSize());

        return page.to(SocDifferenceListVO.class);
    }

    @Override
    public SocDifferenceDO selectDOById(String socDifferenceId, String siteId) {
        return listById(socDifferenceId, siteId);
    }

    @Override
    public List<SocDifferenceDO> selectDO(SocDifferenceQuery socDifferenceQuery, String siteId) {
        Example example = buildExample(socDifferenceQuery, siteId);
        return socDifferenceDAO.selectByExample(example);
    }

    @Override
    public List<SocDifferenceDO> selectByMonthId(String monthId, TokenUser tokenUser) {
        Example example = new Example(SocDifferenceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("monthId", monthId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return socDifferenceDAO.selectByExample(example);
    }

    @Override
    public List<SocDifferenceDO> selectBySocInfoIdList(List<String> socInfoIdList, String siteId) {
        Example example = new Example(SocDifferenceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIn("socInfoId", socInfoIdList)
                .andEqualTo("siteId", siteId);
        return socDifferenceDAO.selectByExample(example);
    }

    @Override
    public void bachDifference(BachDifferenceDTO bachDifferenceDTO, TokenUser tokenUser) {
        String differenceName = bachDifferenceDTO.getDifferenceName();
        if (differenceName.length() > 20) {
            throw new ParamException("差额费用名称不能大于20个字符");
        }
        if (ObjectUtils.isEmpty(bachDifferenceDTO.getCompanyDifference()) &&
                ObjectUtils.isEmpty(bachDifferenceDTO.getPersonalDifference())) {
            throw new ParamException("个人和单位差额费用不允许同时为空");
        }
        EmpSocMonthDO empSocMonth = empSocMonthService
                .selectDOById(bachDifferenceDTO.getMonthId(), tokenUser.getSiteId());
        ParamException.notNull(empSocMonth, "参保月已被删除");
        // 查询出传入参保月内所有缴费类型为正常应缴的人(每个人每个月内,只存在一条正常应缴的记录)
        EmpSocInfoQuery empSocInfoQuery = new EmpSocInfoQuery();
        empSocInfoQuery.setMonthId(bachDifferenceDTO.getMonthId());
        empSocInfoQuery.setCompanyId(bachDifferenceDTO.getCompanyId());
        // 设置查询条件为正常应缴
        empSocInfoQuery.setSocPayCostType(SocPayCostType.NORMAL_PAY_COST.getValue());
        List<String> empIdList = bachDifferenceDTO.getEmpIdList();
        empSocInfoQuery.setEmpIdList(empIdList);
        List<EmpSocInfoListVO> empSocInfoList = empSocInfoService.selectAllByMonthId(empSocInfoQuery, tokenUser);
        // 转变数据结构为   <员工Id, 对应的参保记录> 的map形式
        Map<String, EmpSocInfoListVO> socInfoMap = new HashMap<>(128);
        Map<String, List<SocDifferenceDO>> socInfoDifferenceMap = new HashMap<>(128);
        if (!CollectionUtils.isEmpty(empSocInfoList)) {
            socInfoMap = empSocInfoList
                    .stream().collect(Collectors.toMap(EmpSocInfoListVO::getEmpId, v -> v));
            // 拿到这些参保记录的Id
            List<String> socInfoIdList = empSocInfoList.stream().map(EmpSocInfoListVO::getId).collect(Collectors.toList());
            // 查询这些参保记录对应的的差额费用记录
            List<SocDifferenceDO> differenceBySocInfoList = selectBySocInfoIdList(socInfoIdList, tokenUser.getSiteId());
            // 把它变成 <参保记录Id, 参保记录对应的差额费用记录> 的 map 形式
            if (!CollectionUtils.isEmpty(differenceBySocInfoList)) {
                socInfoDifferenceMap = differenceBySocInfoList.stream()
                        .collect(Collectors.groupingBy(SocDifferenceDO::getSocInfoId));
            }
        }
        List<EmpSocInfoDO> empSocInfoInsertList = new ArrayList<>();
        List<SocDifferenceDO> socDifferenceInsertList = new ArrayList<>();
        List<String> updateList = new ArrayList<>();
        List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService
                .selectByEmpIdList(empIdList, tokenUser.getSiteId());
        Map<String, EmpSocRelatedDO> relatedMap = empSocRelatedList.stream()
                .collect(Collectors.toMap(EmpSocRelatedDO::getEmpId, v -> v));
        for (String empId : empIdList) {
            // 如果没有对应的参保记录需要给他创建一条出来
            EmpSocInfoListVO empSocInfo = socInfoMap.get(empId);
            String socInfoId;
            if (ObjectUtils.isEmpty(empSocInfo)) {
                EmpSocRelatedDO empSocRelated = relatedMap.get(empId);
                EmpSocInfoDO empSocInfoInsert = new EmpSocInfoDO();
                socInfoId = UUID.randomUUID().toString().replace("-", "").toUpperCase();
                empSocInfoInsert.setId(socInfoId);
                empSocInfoInsert.setEmpId(empId);
                empSocInfoInsert.setYear(empSocMonth.getYear());
                empSocInfoInsert.setMonth(empSocMonth.getMonth());
                empSocInfoInsert.setPersonalSocAmount(new BigDecimal("0"));
                empSocInfoInsert.setPersonalPfAmount(new BigDecimal("0"));
                empSocInfoInsert.setCompanySocAmount(new BigDecimal("0"));
                empSocInfoInsert.setCompanyPfAmount(new BigDecimal("0"));
                empSocInfoInsert.setPlanFlag(true);
                empSocInfoInsert.setEmpSocStatus(empSocRelated.getSocState());
                empSocInfoInsert.setEmpMeStatus(empSocRelated.getMeState());
                empSocInfoInsert.setEmpPfStatus(empSocRelated.getPfState());
                empSocInfoInsert.setMonthId(bachDifferenceDTO.getMonthId());
                empSocInfoInsert.setCompanyId(bachDifferenceDTO.getCompanyId());
                empSocInfoInsert.setPayCostType(SocPayCostType.NORMAL_PAY_COST.getValue());
                empSocInfoInsert.preInsert(tokenUser.getUserId());
                empSocInfoInsert.setSiteId(tokenUser.getSiteId());
                empSocInfoInsertList.add(empSocInfoInsert);
            } else {
                socInfoId = empSocInfo.getId();
                // 判断是否 创建过 这种差额费用
                List<SocDifferenceDO> currentDifference = socInfoDifferenceMap.get(socInfoId);
                if (!CollectionUtils.isEmpty(currentDifference)) {
                    List<String> existDifferenceNameList = currentDifference.stream()
                            .map(SocDifferenceDO::getDifferenceName).collect(Collectors.toList());
                    if (existDifferenceNameList.contains(differenceName)) {
                        // 如果创建过这种差额费用做更新操作
                        Map<String, SocDifferenceDO> existDifferenceMap = currentDifference
                                .stream().collect(Collectors.toMap(SocDifferenceDO::getDifferenceName, v -> v));
                        SocDifferenceDO socDifferenceDO = existDifferenceMap.get(differenceName);
                        updateList.add(socDifferenceDO.getId());
                        continue;
                    }
                }
            }
            SocDifferenceDO differenceInsert = new SocDifferenceDO();
            differenceInsert.setMonthId(bachDifferenceDTO.getMonthId());
            differenceInsert.setSocInfoId(socInfoId);
            differenceInsert.setEmpId(empId);
            differenceInsert.setDifferenceName(differenceName);
            if (ObjectUtils.isEmpty(bachDifferenceDTO.getCompanyDifference())) {
                differenceInsert.setCompanyDifference(new BigDecimal("0"));
            } else {
                differenceInsert.setCompanyDifference(bachDifferenceDTO.getCompanyDifference());
            }
            if (ObjectUtils.isEmpty(bachDifferenceDTO.getPersonalDifference())) {
                differenceInsert.setPersonalDifference(new BigDecimal("0"));
            } else {
                differenceInsert.setPersonalDifference(bachDifferenceDTO.getPersonalDifference());
            }
            differenceInsert.setCompanyId(bachDifferenceDTO.getCompanyId());
            differenceInsert.preInsert(tokenUser.getUserId());
            differenceInsert.setSiteId(tokenUser.getSiteId());
            socDifferenceInsertList.add(differenceInsert);
        }
        if (!CollectionUtils.isEmpty(empSocInfoInsertList)) {
            empSocInfoService.bachSave(empSocInfoInsertList);
        }
        if (!CollectionUtils.isEmpty(socDifferenceInsertList)) {
            socDifferenceDAO.insertListAndSetId(socDifferenceInsertList);
        }
        if (!CollectionUtils.isEmpty(updateList)) {
            SocDifferenceDO updateTemplate = new SocDifferenceDO();
            if (!ObjectUtils.isEmpty(bachDifferenceDTO.getCompanyDifference())) {
                updateTemplate.setCompanyDifference(bachDifferenceDTO.getCompanyDifference());
            }
            if (!ObjectUtils.isEmpty(bachDifferenceDTO.getPersonalDifference())) {
                updateTemplate.setPersonalDifference(bachDifferenceDTO.getPersonalDifference());
            }
            Example exampleUpdate = new Example(SocDifferenceDO.class);
            exampleUpdate.and().andIn("id", updateList);
            socDifferenceDAO.updateByExampleSelective(updateTemplate, exampleUpdate);
        }
    }

    @Override
    public List<SocDifferenceDO> selectByEmpAndName(String empId, String differenceName,
                                                    String monthId, TokenUser tokenUser) {
        Example example = new Example(SocDifferenceDO.class);
        example.and()
                .andEqualTo("monthId", monthId)
                .andEqualTo("empId", empId)
                .andEqualTo("differenceName", differenceName)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return socDifferenceDAO.selectByExample(example);
    }

    @Override
    public List<SocDifferenceDO> selectByMonthAndCompanyList(List<String> companyIdList, List<String> monthIdList,
                                                             String siteId) {
        Example example = new Example(SocDifferenceDO.class);
        example.and()
                .andIn("monthId", monthIdList)
                .andIn("companyId", companyIdList)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        return socDifferenceDAO.selectByExample(example);
    }

    @Override
    public List<SocDifferenceDO> selectBySite(TokenUser tokenUser) {
        Example example = new Example(SocDifferenceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return socDifferenceDAO.selectByExample(example);
    }

    /**
     * 根据查询参数，构建example
     *
     * @param socDifferenceQuery 查询参数
     * @param siteId             所属站点id
     * @return example
     */
    private Example buildExample(SocDifferenceQuery socDifferenceQuery, String siteId) {
        Example example = new Example(SocDifferenceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (socDifferenceQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", socDifferenceQuery.getCompanyId());
        }
        if (socDifferenceQuery.getEmpId() != null) {
            example.and().andEqualTo("empId", socDifferenceQuery.getEmpId());
        }
        // 排序
        ExampleKit.setExampleOrder(example, socDifferenceQuery.getOrders());
        return example;
    }
}




